const express = require("express");
const http = require("http");
const { Server } = require("socket.io");
const dotenv = require("dotenv");
const cors = require("cors");
const fs = require("fs");
const path = require("path");
const morgan = require("morgan");
const logger = require("./logger");
const lodash = require("lodash");

dotenv.config();

const app = express();
const server = http.createServer(app);

app.use((err, req, res, next) => {
  logger.error(`服务器异常: ${err.message}`);
  res.status(500).send("服务器异常");
});

// 捕获未捕获的异常
process.on("uncaughtException", (err) => {
  logger.error(err); // 直接记录错误对象，以便包含堆栈信息
});

process.on("unhandledRejection", (reason, promise) => {
  logger.error(reason); // 直接记录拒绝原因，以便包含堆栈信息
});

app.get("/async-error", async (req, res, next) => {
  try {
    // 模拟异步错误
    await someAsyncFunction();
    res.send("This will not be reached if an error occurs");
  } catch (err) {
    next(err); // 将错误传递给错误处理中间件
  }
});

const allowedOrigins = [
  "http://117.72.8.112:9000",
  "http://117.72.8.112:7396",
  "http://localhost:9000",
  "http://192.168.50.44:9000",
  "http://lllkk.fun:9000",
  "https://werewolf.lllkk.fun",
];

const io = new Server(server, {
  cors: {
    origin: (origin, callback) => {
      if (allowedOrigins.indexOf(origin) !== -1 || !origin) {
        callback(null, true);
      } else {
        callback(new Error("Not allowed by CORS"));
      }
    },
    methods: ["GET", "POST"],
  },
  pingInterval: 10000,
  pingTimeout: 5000,
});

app.use(
  cors({
    origin: (origin, callback) => {
      if (allowedOrigins.indexOf(origin) !== -1 || !origin) {
        callback(null, true);
      } else {
        callback(new Error("Not allowed by CORS"));
      }
    },
    methods: ["GET", "POST"], // 可选的，定义允许的方法
  })
);

const rooms = {};
const heartbeats = {}; // 存储心跳检测任务

const baseAbilities = {
  viewPlayer: {
    name: "viewPlayer",
    execute: (game, id, data) => {
      if (data.target1 && data.target1.type === "player") {
        const player = game.players.find(
          (player) => player.username === data.target1.name
        );
        if (player) {
          game.logAction(
            id,
            logType.player.type,
            `查看了玩家${getPlayerIndex(game.players, player.username)}-${
              player.username
            }的身份，他是${player.role.name}`
          );
          return true;
        }
      }
      return false;
    },
  },
  viewDeck: {
    name: "viewDeck",
    execute: (game, id, data) => {
      if (data && data.target1 && data.target1.type === "deck") {
        const index = data.target1.name;
        // 判断是否需要存储被查看的卡牌
        const player = game.players.find((p) => p.id === id);
        if (player) {
          // 校验是否已经验过该卡牌
          if (player.seenArray && player.seenArray.length > 0) {
            if (
              player.seenArray.some(
                (item) =>
                  item.name === data.target1.name &&
                  item.type === data.target1.type
              )
            ) {
              return false;
            }
          }

          // 判断是否需要存储为交换对象
          if (
            player.initialRole.extParams &&
            player.initialRole.extParams.storeView
          ) {
            player.toBeSwap = {};
            player.toBeSwap.type = "deck";
            player.toBeSwap.name = index;
          }
          game.logAction(
            id,
            logType.player.type,
            `查看了底牌${deckName[index]}，他是${game.leftoverCards[index].name}`
          );

          if (!player.seenArray) {
            player.seenArray = [];
          }
          player.seenArray.push(data.target1);

          return true;
        }
      } else {
        return false;
      }
    },
  },

  swapCard: {
    name: "swapCard",
    execute: (game, id, data) => {
      logInfo = "交换了";

      // 如果只有一个对象则默认自己
      if (!data.target2) {
        const currentPlayer = game.players.find((player) => player.id == id);
        data.target2 = {};
        if (currentPlayer.toBeSwap) {
          data.target2.type = currentPlayer.toBeSwap.type;
          data.target2.name = currentPlayer.toBeSwap.name;
        } else {
          data.target2.type = "player";
          data.target2.name = currentPlayer.username;
        }
      }
      if (data.target1.type === "deck") {
        logInfo = logInfo.concat("底牌", deckName[data.target1.name]);
      } else if (data.target1.type === "player") {
        logInfo = logInfo.concat(
          `玩家${getPlayerIndex(game.players, data.target1.name)}-`,
          data.target1.name,
          "的身份"
        );
      }
      logInfo = logInfo.concat(" 与 ");
      if (data.target2.type === "deck") {
        // 换牌逻辑
        if (data.target1.type === "deck") {
          [
            game.leftoverCards[data.target1.name],
            game.leftoverCards[data.target2.name],
          ] = [
            game.leftoverCards[data.target2.name],
            game.leftoverCards[data.target1.name],
          ];
        } else {
          const player = game.players.find(
            (player) => player.username === data.target1.name
          );
          if (!player.lock) {
            [game.leftoverCards[data.target2.name], player.role] = [
              player.role,
              game.leftoverCards[data.target2.name],
            ];
          }
        }

        logInfo = logInfo.concat("底牌", deckName[data.target2.name]);
      } else if (data.target2.type === "player") {
        // 换牌逻辑
        if (data.target1.type === "deck") {
          const player = game.players.find(
            (player) => player.username === data.target2.name
          );
          if (!player.lock) {
            [game.leftoverCards[data.target1.name], player.role] = [
              player.role,
              game.leftoverCards[data.target1.name],
            ];
          }
        } else {
          const player1 = game.players.find(
            (player) => player.username === data.target1.name
          );
          const player2 = game.players.find(
            (player) => player.username === data.target2.name
          );
          if (!player1.lock && !player2.lock) {
            [player1.role, player2.role] = [player2.role, player1.role];
          }
        }

        logInfo = logInfo.concat(
          `玩家${getPlayerIndex(game.players, data.target2.name)}-`,
          data.target2.name,
          "的身份"
        );
      }
      game.logAction(id, logType.player.type, logInfo);
      return true;
    },
  },

  votePlayer: {
    name: "votePlayer",
    execute: (game, id, data) => {
      if (data.target1 && data.target1.type === "player") {
        const currentPlayer = game.players.find((player) => player.id == id);
        const targetPlayer = game.players.find(
          (player) => player.username === data.target1.name
        );
        currentPlayer.target = targetPlayer.username;
        game.logAction(
          id,
          logType.player.type,
          `你指定了玩家${getPlayerIndex(game.players, targetPlayer.username)}-${
            targetPlayer.username
          },他出局你获得胜利`
        );
        return true;
      } else {
        return false;
      }
    },
  },

  lockPlayer: {
    name: "lockPlayer",
    execute: (game, id, data) => {
      if (data.target1 && data.target1.type === "player") {
        const player = game.players.find(
          (player) => player.username === data.target1.name
        );
        if (player) {
          player.lock = true;
          game.logAction(
            id,
            logType.player.type,
            `你指定了锁定了玩家${getPlayerIndex(
              game.players,
              player.username
            )}-${player.username},他不会被交换`
          );
          return true;
        }
      } else {
        return false;
      }
    },
  },

  beWolf: {
    name: "beWolf",
    execute: (game, id, data) => {
      if (data.target1 && data.target1.type === "player") {
        const player = game.players.find(
          (player) => player.username === data.target1.name
        );
        if (player) {
          player.role = defaultRoles.find((role) => role.name === "狼人");
          game.logAction(
            id,
            logType.player.type,
            `你将玩家${getPlayerIndex(game.players, player.username)}-${
              player.username
            }变为了狼人`
          );
          return true;
        }
      } else {
        return false;
      }
    },
  },
};

const nagativeAbilities = {
  seeWolfs: {
    name: "seeWolfs",
    execute: (game, id) => {
      // 狼人确认队友
      const werewolves = game.players.filter(
        (player) => player.initialRole.faction === "wolf"
      );
      if (werewolves.length > 0) {
        const werewolfNames = werewolves
          .map((wolf) => wolf.username)
          .join(", ");
        game.logAction(id, logType.system.type, `本局狼人有: ${werewolfNames}`);
      }
    },
  },
  seeMasons: {
    name: "seeMasons",
    execute: (game, id) => {
      const masons = game.players.filter(
        (player) => player.initialRole.name === "守夜人"
      );
      const masonNames = masons.map((mason) => mason.username).join(", ");
      game.logAction(id, logType.system.type, `本局守夜人有: ${masonNames}`);
    },
  },
  sigleWolf: {
    name: "sigleWolf",
    execute: (game) => {
      // 如果只有一个狼人，允许其查看一张底牌
      const werewolves = game.players.filter(
        (player) => player.initialRole.faction === "wolf"
      );
      if (werewolves.length === 1) {
        werewolves[0].initialRole.abilities.push(abilities.viewDeck);
        game.logAction(
          werewolves[0].id,
          logType.system.type,
          `你是唯一的狼人，你可以查看一张底牌。`
        );
      }
    },
  },
  seeSelf: {
    name: "seeSelf",
    execute: (game, id) => {
      // 失眠者查看自己身份呢
      const player = game.players.find((player) => player.id === id);
      game.logAction(
        id,
        logType.system.type,
        `你现在的身份是${player.role.name}`
      );
    },
  },
  randomMaker: {
    name: "randomMaker",
    execute: (game, id) => {
      const n = game.players.length;

      // 小精灵：根据玩家数量随机交换包括自己的两张牌
      const num1 = getRandomInt(n);
      let num2 = getRandomInt(n);

      // 确保 num2 不与 num1 重复
      while (num2 === num1) {
        num2 = getRandomInt(n);
      }

      player1 = game.players[num1];
      player2 = game.players[num2];

      if (!player1.lock && !player2.lock) {
        [player1.role, player2.role] = [player2.role, player1.role];
      }

      game.logAction(
        id,
        logType.player.type,
        `系统随机交换了玩家${getPlayerIndex(game.players, player1.username)}-${
          player1.username
        }与玩家${getPlayerIndex(game.players, player2.username)}-${
          player2.username
        }的身份`
      );
    },
  },
  sigleMinion: {
    name: "sigleMinion",
    execute: (game) => {
      // 如果没有狼人
      const werewolves = game.players.filter(
        (player) => player.initialRole.faction === "wolf"
      );
      if (werewolves.length == 0) {
        const player = game.players.find(
          (player) => player.initialRole.faction === "minion"
        );
        if (player) {
          player.initialRole.faction = "wolf";
          game.logAction(
            player.id,
            logType.system.type,
            `本局没有狼人,你将转换为狼人阵营`
          );
        }
      }
    },
  },
};

const abilities = {
  viewHand: {
    name: "查看手牌",
    max: 1,
    base: [baseAbilities.viewPlayer],
    opType: ["player"],
  },
  swapHand: {
    name: "交换手牌",
    max: 1,
    base: [baseAbilities.swapCard],
    opType: ["player", "player"],
  },
  randomHandSwap: {
    name: "随机交换两张手牌",
    max: 1,
    nagative: [nagativeAbilities.randomMaker],
  },
  seerViewDeck: {
    name: "预言家查看底牌",
    max: 2,
    base: [baseAbilities.viewDeck],
    opType: ["deck"],
  },
  viewDeck: {
    name: "查看底牌",
    max: 1,
    base: [baseAbilities.viewDeck],
    opType: ["deck"],
  },
  viewAndSwap: {
    name: "查看并交换手牌",
    max: 1,
    base: [baseAbilities.viewPlayer, baseAbilities.swapCard],
    opType: ["player"],
  },
  seeWolfs: { name: "确认狼人", nagative: [nagativeAbilities.seeWolfs] },
  seeMasons: { name: "确认守夜人", nagative: [nagativeAbilities.seeMasons] },
  viewSelfHand: {
    name: "确认自己手牌",
    max: 1,
    nagative: [nagativeAbilities.seeSelf],
  },
  swapSelfHandDeck: {
    name: "交换手牌和任意一张底牌",
    max: 1,
    base: [baseAbilities.swapCard],
    opType: ["deck"],
  },
  viewAndSteal: {
    name: "查看并交换底牌",
    max: 1,
    base: [baseAbilities.viewDeck, baseAbilities.swapCard],
    opType: ["deck"],
  },
  lockPlayer: {
    name: "锁定玩家身份",
    max: 1,
    base: [baseAbilities.lockPlayer],
    opType: ["player"],
  },
  votePlayer: {
    name: "票出该玩家则获胜",
    max: 1,
    base: [baseAbilities.votePlayer],
    opType: ["player"],
  },
  swapDeckAndHand: {
    name: "将一张牌交换给某位玩家",
    max: 1,
    base: [baseAbilities.swapCard],
    opType: ["player"],
    order: 2,
  },
  beWolf: {
    name: "将某位玩家变为狼人",
    max: 1,
    base: [baseAbilities.beWolf],
    opType: ["player"],
  },
  gameBegin: {
    name: "游戏开始时的系统执行能力",
    nagative: [nagativeAbilities.sigleWolf, nagativeAbilities.sigleMinion],
  },
};

const logType = {
  system: { name: "系统提示", type: "1" },
  player: { name: "玩家操作", type: "2" },
};

const defaultRoles = [
  {
    name: "狼人",
    description: "夜晚确认同伴身份,独狼可查看一张底牌",
    img: "/werewolf.png",
    count: 2,
    abilities: [abilities.seeWolfs],
    abilitiesCount: 1,
    faction: "wolf",
    phase: "狼人",
  },
  {
    name: "预言家",
    description: "夜晚可查看一名其他玩家的牌或者两张底牌",
    img: "/seer.png",
    count: 1,
    abilities: [abilities.viewHand, abilities.seerViewDeck],
    abilitiesCount: 1,
    faction: "village",
    phase: "预言家",
  },
  {
    name: "捣蛋鬼",
    description: "夜晚可选择两名其他玩家，交换他们的身份",
    img: "/troublemaker.png",
    count: 1,
    abilities: [abilities.swapHand],
    abilitiesCount: 2,
    faction: "village",
    phase: "捣蛋鬼",
  },
  {
    name: "强盗",
    description: "夜晚可选择一名其他玩家，查看他的身份并与自己交换",
    img: "/robber.png",
    count: 1,
    abilities: [abilities.viewAndSwap],
    abilitiesCount: 1,
    faction: "village",
    phase: "强盗",
  },
  {
    name: "村民",
    description: "没有特殊能力，白天参与投票",
    img: "/villager.png",
    count: 0,
    abilities: [],
    abilities: 0,
    faction: "village",
    phase: "村民",
  },
  {
    name: "失眠者",
    description: "天亮前可查看自己的最新身份",
    img: "/insomniac.png",
    count: 1,
    abilities: [abilities.viewSelfHand],
    abilitiesCount: 0,
    faction: "village",
    phase: "失眠者",
  },
  {
    name: "爪牙",
    description: "夜晚可知道场上的狼人牌，白天被投出去算狼人阵营获胜",
    img: "/minion.png",
    count: 0,
    abilities: [abilities.seeWolfs],
    abilitiesCount: 0,
    faction: "minion",
    phase: "狼人",
  },
  {
    name: "狼先知",
    description: "在狼人基础上，可查看一名其他玩家的牌",
    img: "/mystic.png",
    count: 0,
    abilities: [abilities.viewHand, abilities.seeWolfs],
    abilitiesCount: 2,
    faction: "wolf",
    phase: "狼先知",
  },
  {
    name: "守夜人",
    description: "成对加入对局，夜晚睁眼查看守夜人队友",
    img: "/mason.png",
    count: 0,
    abilities: [abilities.seeMasons],
    abilitiesCount: 0,
    faction: "village",
    phase: "守夜人",
  },
  {
    name: "酒鬼",
    description: "夜晚可选择一张底牌交换给自己，不知道自己的最新身份",
    img: "/drunk.png",
    count: 0,
    abilities: [abilities.swapSelfHandDeck],
    abilitiesCount: 1,
    faction: "village",
    phase: "酒鬼",
  },
  {
    name: "皮匠",
    description: "被投出去则自己单独获胜",
    img: "/Tanner.png",
    count: 0,
    abilities: [],
    abilitiesCount: 0,
    faction: "tanner",
    phase: "皮匠",
  },
  {
    name: "小精灵",
    description: "随机交换两名玩家的身份（包括自己）",
    img: "/newtroublemaker.jpg",
    count: 0,
    abilities: [abilities.randomHandSwap],
    abilitiesCount: 0,
    faction: "village",
    phase: "小精灵",
  },
  {
    name: "盗贼",
    description: "夜晚可选择一张底牌交换给自己，并查看自己的最新身份",
    img: "/curse.png",
    count: 0,
    abilities: [abilities.viewAndSteal],
    abilitiesCount: 1,
    faction: "village",
    phase: "盗贼",
  },
  {
    name: "诅咒者",
    description: "指定一名玩家，该玩家被投出去则自己获胜",
    img: "/curse.jpg",
    count: 0,
    abilities: [abilities.votePlayer],
    abilitiesCount: 1,
    faction: "curse",
    phase: "诅咒者",
  },
  {
    name: "哨兵",
    description: "守护一名玩家，使其身份不会被换",
    img: "/sentinel.png",
    count: 0,
    abilities: [abilities.lockPlayer],
    abilitiesCount: 1,
    faction: "village",
    phase: "哨兵",
  },
  {
    name: "见习预言家",
    description: "夜晚可查看一张底牌",
    img: "/apprentice.png",
    count: 0,
    abilities: [abilities.viewDeck],
    abilitiesCount: 1,
    faction: "village",
    phase: "预言家",
  },
  {
    name: "女巫",
    description: "查看一张底牌并将他交换给某位玩家",
    img: "/witch.png",
    count: 0,
    abilities: [abilities.viewDeck, abilities.swapDeckAndHand],
    extParams: { storeView: true },
    abilitiesCount: 2,
    faction: "village",
    phase: "女巫",
  },
  {
    name: "阿尔法狼",
    description: "将某位玩家变成狼人",
    img: "/alpha.jpg",
    count: 0,
    abilities: [abilities.seeWolfs, abilities.beWolf],
    abilitiesCount: 2,
    faction: "wolf",
    phase: "阿尔法狼",
  },
  {
    name: "冲锋狼",
    description: "投票结算时少算一票",
    img: "/crashwolf.png",
    count: 0,
    abilities: [abilities.seeWolfs],
    abilitiesCount: 1,
    faction: "wolf",
    phase: "狼人",
  },
];

const majorPhases = ["夜晚", "白天"];
const nightSubPhases = [
  {
    name: "准备阶段",
    actionTime: 10,
  },
  {
    name: "爪牙",
    actionTime: 0,
  },
  {
    name: "狼人",
    actionTime: 8,
  },
  {
    name: "狼先知",
    actionTime: 15,
  },
  {
    name: "阿尔法狼",
    actionTime: 15,
  },
  {
    name: "守夜人",
    actionTime: 0,
  },
  {
    name: "诅咒者",
    actionTime: 8,
  },
  {
    name: "预言家",
    actionTime: 10,
  },
  {
    name: "见习预言家",
    actionTime: 8,
  },
  {
    name: "哨兵",
    actionTime: 8,
  },
  {
    name: "强盗",
    actionTime: 8,
  },
  {
    name: "女巫",
    actionTime: 10,
  },
  {
    name: "捣蛋鬼",
    actionTime: 10,
  },
  {
    name: "小精灵",
    actionTime: 0,
  },
  {
    name: "酒鬼",
    actionTime: 8,
  },
  {
    name: "盗贼",
    actionTime: 8,
  },
  {
    name: "失眠者",
    actionTime: 0,
  },
];
const daySubPhases = ["讨论环节", "投票环节", "结算环节"];
const deckName = ["A", "B", "C"];

const calculateBeforeFilter = [
  {
    name: "crashWolf",
    filt: (game, voteCounts) => {
      // 找到这把玩家中的冲锋狼
      player = game.players.find((p) => p.role.name === "冲锋狼");
      if (player) {
        // 票数减1
        const vote = voteCounts[player.username];
        if (vote) {
          voteCounts[player.username] = vote - 1;
        }
      }
    },
  },
];

const victoryConditions = {
  1: {
    name: "minion",
    condition: (game, mostVotedPlayers) => {
      const minionPlayer = game.players.find(
        (p) => p.role.faction === "minion"
      );
      return (
        minionPlayer &&
        mostVotedPlayers.some(
          (playerName) => playerName === minionPlayer.username
        )
      );
    },
    desc: "狼人阵营",
  },
  2: {
    name: "tanner",
    condition: (game, mostVotedPlayers) => {
      const tannerPlayer = game.players.find(
        (p) => p.role.faction === "tanner"
      );
      return (
        tannerPlayer &&
        mostVotedPlayers.some(
          (playerName) => playerName === tannerPlayer.username
        )
      );
    },
    desc: "皮匠",
  },
  3: {
    name: "village",
    condition: (game, mostVotedPlayers) => {
      const wolfs = game.players
        .filter((p) => p.role.faction === "wolf")
        .map((p) => p.username);
      return (
        wolfs.length == 0 ||
        mostVotedPlayers.some((playerName) => wolfs.includes(playerName))
      );
    },
    desc: "好人阵营",
  },
  4: {
    name: "wolf",
    condition: (game, mostVotedPlayers) => {
      const wolfs = game.players
        .filter((p) => p.role.faction === "wolf")
        .map((p) => p.username);
      return (
        wolfs.length > 0 &&
        mostVotedPlayers.every((playerName) => !wolfs.includes(playerName))
      );
    },
    desc: "狼人阵营",
  },
};

const separateVictory = [
  {
    name: "curse",
    condition: (game, mostVotedPlayers) => {
      // 如果有诅咒者诅咒的目标 则自己获胜
      const cursePlayer = game.players.find((p) => p.target);

      return (
        cursePlayer &&
        mostVotedPlayers.some((playerName) => cursePlayer.target === playerName)
      );
    },
    desc: "诅咒者",
  },
];

class Game {
  constructor(room) {
    this.room = room;
    this.preRoles = [];
    this.players = [];
    this.leftoverCards = [];
    this.majorPhase = "夜晚";
    this.subPhase = "狼人";
    this.logs = {}; // 操作记录
    this.voteResults = []; // 投票结果
    this.discussionInfo = null;
    this.started = false;
  }

  addPlayer(player) {
    player.actions = {}; // 初始化 actions 属性
    this.players.push(player);
  }

  setLeftoverCards(cards) {
    this.leftoverCards = cards;
  }

  logAction(playerId, type, action) {
    const player = this.players.find((p) => p.id === playerId);

    if (player) {
      if (!this.logs[player.username]) {
        this.logs[player.username] = {};
      }
      if (!this.logs[player.username][type]) {
        this.logs[player.username][type] = [];
      }
      this.logs[player.username][type].push(action);
    }
  }

  logVoteResult(playerId, targetId) {
    const currentName = this.players.find((p) => p.id === playerId)?.username;
    const targetName = this.players.find((p) => p.id === targetId)?.username;

    this.voteResults.push({ currentName, targetName });
  }

  getState() {
    return {
      players: this.players,
      leftoverCards: this.leftoverCards,
      majorPhase: this.majorPhase,
      subPhase: this.subPhase,
      logs: this.logs, // 返回操作记录
      voteResults: this.voteResults, // 返回投票结果
      discussionInfo: this.discussionInfo,
      started: this.started,
      nightSubPhases: this.phases, // 行动顺序
      curActionTime:
        this.started && this.majorPhase === "夜晚"
          ? nightSubPhases.find((sub) => sub.name == this.subPhase).actionTime -
            Math.round((Date.now() - this.nextPhaseTaskTime) / 1000)
          : null,
      winner: this.winner,
    };
  }

  startDiscussionPhase() {
    const randomPlayerIndex = Math.floor(Math.random() * this.players.length);
    const directions = ["顺时针", "逆时针"];
    const randomDirection =
      directions[Math.floor(Math.random() * directions.length)];

    this.discussionInfo = {
      startingPlayer: this.players[randomPlayerIndex],
      direction: randomDirection,
      index: randomPlayerIndex,
    };
  }
}

// 随机洗牌函数
function shuffle(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}

function getRandomInt(max) {
  return Math.floor(Math.random() * max);
}

function getActionTime() {
  return;
}

/**
 * 获胜判定
 * @param {投票结果} voteCounts
 * @param {游戏对象} game
 * @returns
 */
function checkVictory(voteCounts, game) {
  const maxVotes = Math.max(...Object.values(voteCounts));
  const mostVotedPlayers = Object.keys(voteCounts).filter(
    (playerName) => voteCounts[playerName] === maxVotes
  );

  const victoryGroup = [];

  // 循环遍历结算规则

  // 1.1 单独结算判定
  separateVictory.forEach((vc) => {
    if (vc.condition(game, mostVotedPlayers)) {
      victoryGroup.push(vc.desc);
    }
  });

  // 1.2 综合判定
  const keys = Object.keys(victoryConditions).map((key) => parseInt(key));
  keys.sort((a, b) => a - b);
  keys.some((key) => {
    vc = victoryConditions[key];
    if (vc.condition(game, mostVotedPlayers)) {
      victoryGroup.push(vc.desc);
      return true;
    }
  });

  return victoryGroup;
}

function nigAction(room) {
  const game = rooms[room].game;

  subPhase = nightSubPhases.find((sp) => sp.name === game.subPhase);
  // 查找当前阶段是否有玩家
  const players = game.players.filter(
    (player) =>
      player.initialRole.phase && subPhase.name === player.initialRole.phase
  );

  // 执行被动
  if (players) {
    players.forEach((curPlayer) => {
      curPlayer.initialRole.abilities.forEach((ability) => {
        if (ability.nagative) {
          ability.nagative.forEach((na) => {
            nagativeAbilities[na.name].execute(game, curPlayer.id);
          });
        }
      });
    });
  }

  if (subPhase.actionTime > 0) {
    if (!game.nextPhaseTask) {
      // 开启一个定时任务，结束时推进到下一个阶段
      const intervalId = setTimeout(() => {
        // 等待推进到下一个阶段
        game.nextPhaseTask = null;
        nextPhase(room);
      }, subPhase.actionTime * 1000);
      // 确保只有一个
      game.nextPhaseTask = intervalId;
      game.nextPhaseTaskTime = Date.now();
    }
  } else {
    // 直接推进到下一个阶段
    nextPhase(room);
  }
}

/**
 * 推荐到下一个阶段
 * 夜晚阶段设置推进倒计时，到时间自动推进到下一个阶段
 * @param {} room
 */
function nextPhase(room) {
  if (rooms[room]) {
    const game = rooms[room].game;

    // 检查是否已经存在定时任务并清除
    if (game.nextPhaseTask) {
      clearTimeout(game.nextPhaseTask);
      game.nextPhaseTask = null;
    }

    if (game.majorPhase === "夜晚") {
      currentSubPhaseIndex =
        game.phases.indexOf(game.subPhase) > -1
          ? game.phases.indexOf(game.subPhase)
          : -1;
      if (currentSubPhaseIndex < game.phases.length - 1) {
        game.subPhase = game.phases[++currentSubPhaseIndex];
        nigAction(room);
      } else {
        game.startDiscussionPhase();
        game.majorPhase = "白天";
        game.subPhase = daySubPhases[0];
      }
    } else if (game.majorPhase === "白天") {
      const currentSubPhaseIndex = daySubPhases.indexOf(game.subPhase);
      if (currentSubPhaseIndex < daySubPhases.length - 1) {
        game.subPhase = daySubPhases[currentSubPhaseIndex + 1];
      }
    }

    if (game.subPhase === "结算环节" && !game.victoryGroup) {
      vote(game);
    }

    io.to(room).emit("updateGameState", game.getState());
  }
}

function vote(game) {
  // 所有人投票完成，计算票数并确定胜利者
  const voteCounts = {};
  game.voteResults.forEach((vote) => {
    voteCounts[vote.targetName] = (voteCounts[vote.targetName] || 0) + 1;
  });

  // 先做结算前置条件过滤
  calculateBeforeFilter.forEach((filter) => filter.filt(game, voteCounts));
  game.winner = checkVictory(voteCounts, game).join("、") + "获胜";
  game.subPhase = "结算环节";
}

function getPlayerIndex(players, username) {
  const playerIndex = players.findIndex((p) => p.username === username);
  return playerIndex + 1;
}

io.on("connection", (socket) => {
  console.log("a user connected", socket.id);

  // 重新连接后需要查询并修改状态

  socket.on("updatePlayer", ({ room, player }) => {
    if (rooms[room]) {
      const game = rooms[room].game;
      const currentPlayer = game.players.find(
        (p) => p.username === player.username
      );
      if (currentPlayer) {
        currentPlayer.avatar = player.avatar;
        io.to(room).emit("updatePlayers", game.players);
      }
    }
  });

  socket.on("createRoom", ({ id, username, avatar }, callback) => {
    if (id) {
      const game = new Game(id);
      rooms[id] = { host: socket.id, game, roles: defaultRoles };
      game.addPlayer({ id: socket.id, username, visibleCards: [], avatar });
      socket.join(id);
      callback({ status: "ok", id, roles: defaultRoles, host: socket.id });
      io.to(id).emit("updatePlayers", game.players); // 广播房主的玩家信息
      io.to(id).emit("updateRoles", defaultRoles); // 广播默认角色配置信息
    } else {
      callback({ status: "error", message: "无法创建房间" });
    }
  });

  socket.on("joinRoom", ({ room, username, avatar }, callback) => {
    if (rooms[room]) {
      const game = rooms[room].game;
      const playerExists = game.players.some(
        (player) => player.username === username
      );
      let notify = false;
      if (!playerExists) {
        if (!game.started && username && username.trim().length !== 0) {
          game.addPlayer({ id: socket.id, username, visibleCards: [], avatar });
          io.to(room).emit("updatePlayers", game.players);
          notify = true;
        }
      } else {
        const player = game.players.find(
          (player) => player.username === username
        );

        if (player) {
          // 判断是否还是房主.更换房间的房主id
          if (player.id === rooms[room].host) {
            rooms[room].host = socket.id;
            io.to(room).emit("updateHost", rooms[room].host);
          }
          player.id = socket.id;
          player.offline = false;
          player.avatar = avatar;
          notify = true;
        }
      }

      if (notify) {
        socket.join(room);
        socket.emit("updateGameState", game.getState());
      }
      callback({
        status: "ok",
        roles: rooms[room].roles,
        players: game.players,
        host: rooms[room].host,
      });
    } else {
      callback({ status: "error", message: "房间不存在" });
    }
  });

  socket.on("getRoomData", ({ room }, callback) => {
    if (rooms[room]) {
      const game = rooms[room].game;
      callback({
        players: game.players,
        roles: rooms[room].roles,
        host: rooms[room].host,
      });
    } else {
      callback({ status: "error", message: "房间不存在" });
    }
  });

  socket.on("updateRoles", ({ room, roles }) => {
    if (rooms[room]) {
      rooms[room].roles = roles;
      io.to(room).emit("updateRoles", roles);
    }
  });

  socket.on("addPlayerSlot", (room) => {
    if (rooms[room]) {
      const game = rooms[room].game;
      game.addPlayer({ id: null, username: null, visibleCards: [], avatar: null });
      io.to(room).emit("updatePlayers", game.players); // 广播玩家更新信息
    }
  });

  socket.on("removePlayerSlot", (room) => {
    if (rooms[room]) {
      const game = rooms[room].game;
      if (game.players.length > 0) {
        game.players.pop();
        io.to(room).emit("updatePlayers", game.players); // 广播玩家更新信息
      }
    }
  });

  socket.on("removePlayer", ({ room, index }) => {
    if (rooms[room]) {
      const game = rooms[room].game;
      const player = game.players[index];
      if (player && player.id !== socket.id) {
        game.players.splice(index, 1);
        io.to(room).emit("updatePlayers", game.players); // 广播玩家更新信息
      }
    }
  });

  socket.on("leaveRoom", ({ room, username }) => {
    if (rooms[room]) {
      const game = rooms[room].game;
      const playerIndex = game.players.findIndex(
        (p) => p.username === username
      );
      if (playerIndex !== -1) {
        game.players.splice(playerIndex, 1);
        io.to(room).emit("updatePlayers", game.players); // 广播玩家更新信息
        if (rooms[room].host === socket.id && game.players.length > 0) {
          rooms[room].host = game.players[0].id;
          io.to(room).emit("updateHost", rooms[room].host);
          io.to(game.players[0].id).emit("newHost");
        }

        if (game.players.length === 0) {
          delete rooms[room];
        }
      }
    }
    socket.leave(room);
  });

  socket.on("startGame", ({ room }, callback) => {
    if (rooms[room]) {
      const game = rooms[room].game;
      const roles = rooms[room].roles || defaultRoles; // 确保 roles 定义正确

      // 使用房主的配置初始化角色
      const allRoles = [];
      roles.forEach((role) => {
        for (let i = 0; i < role.count; i++) {
          allRoles.push(lodash.cloneDeep(role));
        }
      });

      // 检查配置是否符合要求
      if (allRoles.length != game.players.length + 3) {
        callback({ status: "error", message: "配置数必须是玩家数+3" });
        return;
      }

      // 检查配置是否符合要求
      if (
        allRoles.filter(
          (role) => role.faction === "wolf" || role.faction === "minion"
        ).length === 0
      ) {
        callback({
          status: "error",
          message: "配置中至少需要有一个狼人阵营或爪牙",
        });
        return;
      }

      game.preRoles = roles;

      let shuffledRoles = shuffle(allRoles);
      // 确保至少有一个狼人或者爪牙
      while (
        shuffledRoles
          .slice(0, game.players.length)
          .filter(
            (role) => role.faction === "wolf" || role.faction === "minion"
          ).length === 0
      ) {
        shuffledRoles = shuffle(allRoles);
      }
      const players = game.players.map((player, index) => ({
        ...player,
        role: shuffledRoles[index],
        initialRole: shuffledRoles[index], // 添加初始角色
        actions: {}, // 初始化 actions 属性
      }));

      const leftoverCards = shuffledRoles
        .slice(players.length)
        .map((card) => ({ ...card, seen: false }));

      game.players = players;
      game.leftoverCards = leftoverCards;

      // 初始化阶段列表
      const assignedRoles = players
        .map((player) => player.role.phase)
        .concat(leftoverCards.map((card) => card.phase));
      const originLeftoverCards = leftoverCards.map((card) => ({
        ...card,
      }));

      game.originLeftoverCards = originLeftoverCards;

      game.phases = nightSubPhases
        .map((action) => action.name)
        .filter((action) => assignedRoles.includes(action));

      // 增加准备阶段
      game.phases.unshift("准备阶段");

      game.majorPhase = "夜晚";
      game.subPhase = "系统";

      // 执行系统能力
      if (abilities.gameBegin.nagative) {
        abilities.gameBegin.nagative.forEach((ability) => {
          ability.execute(game);
        });
      }

      nextPhase(room);

      game.started = true;
      io.to(room).emit("gameStarted", game.getState());
      callback({ status: "ok", roles: roles });
    }
  });

  socket.on("nightAction", ({ room, action, data }, callback) => {
    if (rooms[room]) {
      if (!data || data.length === 0) {
        callback({ status: "error", message: "请选择正确的操作对象" });
      }

      // 操作对象不能相同
      if (
        data.target1 &&
        data.target2 &&
        data.target1.type === data.target2.type &&
        data.target1.name === data.target2.name
      ) {
        callback({ status: "error", message: "不能选择同一个操作对象" });
      }

      const game = rooms[room].game;
      const currentPlayer = game.players.find((p) => p.id === socket.id);

      // 1.校验当前用户是否拥有该能力，以及是否到达触发能力的阶段
      const playerAbilities = currentPlayer.initialRole.abilities;
      if (
        game.majorPhase === "夜晚" &&
        playerAbilities.some((a) => a.name === action) &&
        game.subPhase === currentPlayer.initialRole.phase
      ) {
        // 1.2 再次校验是否达到次数上限 第一是看当前能力操作次数是否达到上限 其次得看总的能力是否达到上限，最后看当前操作的能力是否还没操作完成
        const ability = playerAbilities.find((a) => a.name === action);
        if (
          ability.max > (currentPlayer.actions[action] || 0) &&
          (currentPlayer.executingName == null ||
            ability.name === currentPlayer.executingName) &&
          currentPlayer.initialRole.abilitiesCount >
            (currentPlayer.finishedAbilities || 0)
        ) {
          // 1.3 判断是否满足执行顺序
          if (
            ability.order &&
            ability.order != (currentPlayer.finishedAbilities || 0) + 1
          ) {
            callback({ status: "error", message: "请先完成前置操作" });
            return;
          }

          ability.base.forEach((base) => {
            if (baseAbilities[base.name].execute(game, socket.id, data)) {
              currentPlayer.actions[action] =
                (currentPlayer.actions[action] || 0) + 1;
            }
          });

          // 如果操作次数达到上限 增加一次完成能力的次数
          if (
            currentPlayer.actions &&
            ability.max === currentPlayer.actions[action]
          ) {
            currentPlayer.finishedAbilities =
              (currentPlayer.finishedAbilities || 0) + 1;
            currentPlayer.executingName = null;
          } else {
            currentPlayer.executingName = ability.name;
          }
          socket.emit("updateGameState", game.getState()); // 仅向当前玩家广播游戏状态更新，包括操作记录
        } else {
          callback({ status: "error", message: "操作次数已用完" });
        }
      }
    }
  });

  socket.on("nextPhase", ({ room }) => {
    nextPhase(room);
  });

  // 处理投票逻辑
  socket.on("vote", ({ room, targetId }, callback) => {
    if (rooms[room]) {
      const game = rooms[room].game;
      const player = game.players.find((p) => p.id === socket.id);
      if (!player || player.hasVoted) return;

      const targetPlayer = game.players.find((p) => p.id === targetId);
      game.voteResults.push({
        playerName: player.username,
        targetName: targetPlayer.username,
      });
      player.hasVoted = true;
      game.logAction(
        socket.id,
        logType.system.type,
        `投票了玩家${getPlayerIndex(game.players, targetPlayer.username)}-${
          targetPlayer.username
        }`
      );

      callback({ status: "ok", message: "投票成功" });

      if (game.voteResults.length === game.players.length) {
        vote(game);
      }
      io.to(room).emit("updateGameState", game.getState());
    }
  });

  socket.on("resetGame", ({ room }) => {
    const game = rooms[room].game;
    if (!game) return;

    game.players.forEach((player) => {
      player.role = null;
      player.initialRole = null;
      player.hasVoted = false;
      player.actions = {};
      player.executingName = null;
      player.finishedAbilities = 0;
      player.nagativeExecuted = null;
      player.lock = null;
      player.target = null;
      player.seenArray = null;
    });

    game.majorPhase = "夜晚";
    game.subPhase = "";
    game.voteResults = [];
    game.logs = {};
    game.leftoverCards = [];
    game.winner = null;
    game.discussionInfo = null;
    game.started = false;
    game.victoryGroup = null;

    io.to(room).emit("restartGame", game);
  });

  socket.on("heartbeat", (username) => {
    if (heartbeats[username]) {
      clearInterval(heartbeats[username].intervalId);
      delete heartbeats[username];
      console.log("清除定时任务");
    }
  });

  socket.on("heartbeat2", ({ room, username }) => {
    if (rooms[room]) {
      const game = rooms[room].game;
      const player = game.players.find(
        (player) => player.username === username
      );
      if (player) {
        player.offline = false;
        io.to(room).emit("updatePlayers", game.players);
      }
      io.to(socket.id).emit("updateGameState", game.getState());
    }
  });

  socket.on("disconnect", () => {
    console.log("user disconnected", socket.id);
    for (const room of Object.keys(rooms)) {
      const game = rooms[room].game;
      // 不移除 只更新状态
      const player = game.players.find((p) => p.id === socket.id);
      if (player) {
        player.offline = true;
        //game.players.splice(playerIndex, 1);
        io.to(room).emit("updatePlayers", game.players); // 广播玩家更新信息

        if (rooms[room].host === socket.id) {
          // 这里不采取直接交接的方式 而是创建一个定时任务检测30s是否重连
          let elapsed = 0;
          const intervalId = setInterval(() => {
            elapsed += 5000; // 每5秒检查一次
            console.log(
              `Checking status for ${player.username} (${
                elapsed / 1000
              }s elapsed)`
            );
            if (!player.offline) {
              clearInterval(intervalId);
              delete heartbeats[player.username];
            }
            if (elapsed >= 30000) {
              // 超过30秒后停止检查，并进行房主交接
              clearInterval(intervalId);
              const onlinePlayer = game.players.find((p) => !p.offline);
              if (onlinePlayer) {
                rooms[room].host = onlinePlayer.id;
                io.to(room).emit("updateHost", rooms[room].host);
                io.to(onlinePlayer.id).emit("newHost");
              }
              delete heartbeats[player.username];
            }
          }, 5000); // 每5秒检查一次
          heartbeats[player.username] = { intervalId };
        }

        if (
          game.players.every((player) => player.offline === true) &&
          rooms.length > 50
        ) {
          const roomToDelete = Object.keys(rooms).find((game) =>
            game.players.every((player) => player.offline === true)
          );
          if (roomToDelete) {
            delete rooms[roomToDelete];
          }
        }
      }
    }
  });
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
